#include "wx/wxprec.h"

#if wxUSE_DATAOBJ

#include "wx/dataobj.h"
#include "wx/mstream.h"
#include "wx/uri.h"
#include "wx/gtk/private.h"

GdkAtom  g_textAtom        = 0;
GdkAtom  g_altTextAtom     = 0;
GdkAtom  g_pngAtom         = 0;
GdkAtom  g_fileAtom        = 0;

wxDataFormat::wxDataFormat() {
  m_type = wxDF_INVALID;
  m_format = ( GdkAtom ) 0;
}

wxDataFormat::wxDataFormat( wxDataFormatId type ) {
  PrepareFormats();
  SetType( type );
}

wxDataFormat::wxDataFormat( const wxChar *id ) {
  PrepareFormats();
  SetId( id );
}

wxDataFormat::wxDataFormat( const wxString &id ) {
  PrepareFormats();
  SetId( id );
}

wxDataFormat::wxDataFormat( NativeFormat format ) {
  PrepareFormats();
  SetId( format );
}

void wxDataFormat::SetType( wxDataFormatId type ) {
  PrepareFormats();
  m_type = type;
  if( m_type == wxDF_UNICODETEXT ) {
    m_format = g_textAtom;
  } else if( m_type == wxDF_TEXT ) {
    m_format = g_altTextAtom;
  } else if( m_type == wxDF_BITMAP ) {
    m_format = g_pngAtom;
  } else if( m_type == wxDF_FILENAME ) {
    m_format = g_fileAtom;
  } else
  { wxFAIL_MSG( wxT( "invalid dataformat" ) ); }
}

wxDataFormatId wxDataFormat::GetType() const {
  return m_type;
}

wxString wxDataFormat::GetId() const {
  wxGtkString atom_name( gdk_atom_name( m_format ) );
  return wxString::FromAscii( atom_name );
}

void wxDataFormat::SetId( NativeFormat format ) {
  PrepareFormats();
  m_format = format;
  if( m_format == g_textAtom ) {
    m_type = wxDF_UNICODETEXT;
  } else if( m_format == g_altTextAtom ) {
    m_type = wxDF_TEXT;
  } else if( m_format == g_pngAtom ) {
    m_type = wxDF_BITMAP;
  } else if( m_format == g_fileAtom ) {
    m_type = wxDF_FILENAME;
  } else
  { m_type = wxDF_PRIVATE; }
}

void wxDataFormat::SetId( const wxChar *id ) {
  PrepareFormats();
  m_type = wxDF_PRIVATE;
  wxString tmp( id );
  m_format = gdk_atom_intern( ( const char* ) tmp.ToAscii(), FALSE );
}

void wxDataFormat::PrepareFormats() {
  if( !g_textAtom ) {
    g_textAtom = gdk_atom_intern( "UTF8_STRING", FALSE );
    g_altTextAtom = gdk_atom_intern( "STRING", FALSE );
  }
  if( !g_pngAtom ) {
    g_pngAtom = gdk_atom_intern( "image/png", FALSE );
  }
  if( !g_fileAtom ) {
    g_fileAtom = gdk_atom_intern( "text/uri-list", FALSE );
  }
}

wxDataObject::wxDataObject() {
}

wxDataObject::~wxDataObject() {
}

bool wxDataObject::IsSupportedFormat( const wxDataFormat& format, Direction dir ) const {
  size_t nFormatCount = GetFormatCount( dir );
  if( nFormatCount == 1 ) {
    return format == GetPreferredFormat();
  } else {
    wxDataFormat *formats = new wxDataFormat[nFormatCount];
    GetAllFormats( formats, dir );
    size_t n;
    for( n = 0; n < nFormatCount; n++ ) {
      if( formats[n] == format ) {
        break;
      }
    }
    delete [] formats;
    // found?
    return n < nFormatCount;
  }
}

// ----------------------------------------------------------------------------
// wxTextDataObject
// ----------------------------------------------------------------------------

#if defined(__WXGTK20__) && wxUSE_UNICODE
void wxTextDataObject::GetAllFormats( wxDataFormat *formats, wxDataObjectBase::Direction dir ) const {
  *formats++ = GetPreferredFormat();
  *formats = g_altTextAtom;
}
#endif

// ----------------------------------------------------------------------------
// wxFileDataObject
// ----------------------------------------------------------------------------

bool wxFileDataObject::GetDataHere( void *buf ) const {
  wxString filenames;
  for( size_t i = 0; i < m_filenames.GetCount(); i++ ) {
    filenames += wxT( "file:" );
    filenames += m_filenames[i];
    filenames += wxT( "\r\n" );
  }
  memcpy( buf, filenames.mbc_str(), filenames.length() + 1 );
  return true;
}

size_t wxFileDataObject::GetDataSize() const {
  size_t res = 0;
  for( size_t i = 0; i < m_filenames.GetCount(); i++ ) {
    // This is junk in UTF-8
    res += m_filenames[i].length();
    res += 5 + 2; // "file:" (5) + "\r\n" (2)
  }
  return res + 1;
}

bool wxFileDataObject::SetData( size_t ( size ), const void *buf ) {
  // we get data in the text/uri-list format, i.e. as a sequence of URIs
  // (filenames prefixed by "file:") delimited by "\r\n". size includes
  // the trailing zero (in theory, not for Nautilus in early GNOME
  // versions).
  m_filenames.Empty();
  const gchar *nexttemp = ( const gchar* ) buf;
  for( ; ; ) {
    int len = 0;
    const gchar *temp = nexttemp;
    for( ;; ) {
      if( temp[len] == 0 ) {
        if( len > 0 ) {
          // if an app omits '\r''\n'
          nexttemp = temp + len;
          break;
        }
        return true;
      }
      if( temp[len] == '\r' ) {
        if( temp[len + 1] == '\n' ) {
          nexttemp = temp + len + 2;
        } else
        { nexttemp = temp + len + 1; }
        break;
      }
      len++;
    }
    if( len == 0 ) {
      break;
    }
    // required to give it a trailing zero
    gchar *uri = g_strndup( temp, len );
    gchar *fn = g_filename_from_uri( uri, NULL, NULL );
    g_free( uri );
    if( fn ) {
      AddFile( wxConvFileName->cMB2WX( fn ) );
      g_free( fn );
    }
  }
  return true;
}

void wxFileDataObject::AddFile( const wxString &filename ) {
  m_filenames.Add( filename );
}

// ----------------------------------------------------------------------------
// wxBitmapDataObject
// ----------------------------------------------------------------------------

wxBitmapDataObject::wxBitmapDataObject() {
  Init();
}

wxBitmapDataObject::wxBitmapDataObject( const wxBitmap& bitmap )
  : wxBitmapDataObjectBase( bitmap ) {
  Init();
  DoConvertToPng();
}

wxBitmapDataObject::~wxBitmapDataObject() {
  Clear();
}

void wxBitmapDataObject::SetBitmap( const wxBitmap &bitmap ) {
  ClearAll();
  wxBitmapDataObjectBase::SetBitmap( bitmap );
  DoConvertToPng();
}

bool wxBitmapDataObject::GetDataHere( void *buf ) const {
  if( !m_pngSize ) {
    wxFAIL_MSG( wxT( "attempt to copy empty bitmap failed" ) );
    return false;
  }
  memcpy( buf, m_pngData, m_pngSize );
  return true;
}

bool wxBitmapDataObject::SetData( size_t size, const void *buf ) {
  Clear();
  wxCHECK_MSG( wxImage::FindHandler( wxBITMAP_TYPE_PNG ) != NULL,
               false, wxT( "You must call wxImage::AddHandler(new wxPNGHandler); to be able to use clipboard with bitmaps!" ) );
  m_pngSize = size;
  m_pngData = malloc( m_pngSize );
  memcpy( m_pngData, buf, m_pngSize );
  wxMemoryInputStream mstream( ( char* ) m_pngData, m_pngSize );
  wxImage image;
  if( !image.LoadFile( mstream, wxBITMAP_TYPE_PNG ) ) {
    return false;
  }
  m_bitmap = wxBitmap( image );
  return m_bitmap.Ok();
}

void wxBitmapDataObject::DoConvertToPng() {
  if( !m_bitmap.Ok() ) {
    return;
  }
  wxCHECK_RET( wxImage::FindHandler( wxBITMAP_TYPE_PNG ) != NULL,
               wxT( "You must call wxImage::AddHandler(new wxPNGHandler); to be able to use clipboard with bitmaps!" ) );
  wxImage image = m_bitmap.ConvertToImage();
  wxCountingOutputStream count;
  image.SaveFile( count, wxBITMAP_TYPE_PNG );
  m_pngSize = count.GetSize() + 100; // sometimes the size seems to vary ???
  m_pngData = malloc( m_pngSize );
  wxMemoryOutputStream mstream( ( char* ) m_pngData, m_pngSize );
  image.SaveFile( mstream, wxBITMAP_TYPE_PNG );
}

#endif // wxUSE_DATAOBJ
